#ifndef PERLIN_H
#define PERLIN_H

#include<QVector3D>
#include<utils.h>
class perlin
{
private:
    QVector3D*ranvec;
    int*perm_x;
    int*perm_y;
    int*perm_z;

public:
    perlin(){
             ranvec=perlin_generate();
             perm_x=perlin_generate_perm();
             perm_y=perlin_generate_perm();
             perm_z=perlin_generate_perm();
    }
    float noise(const QVector3D&p)const
    {
        float u=p.x()-floor(p.x());
        float v=p.y()-floor(p.y());
        float w=p.z()-floor(p.z());
        int i=floor(p.x());
        int j=floor(p.y());
        int k=floor(p.z());
        QVector3D c[2][2][2];
        for(int di=0;di<2;di++)
            for(int dj=0;dj<2;dj++)
                for(int dk=0;dk<2;dk++)
                    c[di][dj][dk]=ranvec[perm_x[(i+di)&255]^perm_y[(j+dj)&255]^perm_z[(k+dk)&255]];
        return perlin_interp(c,u,v,w);
    }
    float perlin_interp(QVector3D c[2][2][2],float u,float v,float w)const
    {
        float uu=u*u*(3-2*u);
        float vv=v*v*(3-2*v);
        float ww=w*w*(3-2*w);
        float accum=0;
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                {
                    QVector3D weight_v(u-i,v-j,w-k);
                    accum+=(i*uu+(1-i)*(1-uu))*(j*vv+(1-j)*(1-vv))*(k*ww+(1-k)*(1-ww))*QVector3D::dotProduct(c[i][j][k],weight_v);
                }
        return accum;
    }
    QVector3D*perlin_generate()
    {
        QVector3D *p=new QVector3D[256];
        for(int i=0;i<256;++i)
        {
            p[i]=QVector3D(Random::get(-0.99,0.99),Random::get(-0.99,0.99),Random::get(-0.99,0.99)).normalized();

        }
        return p;
    }
    void permute(int*p,int n)
   {
       for(int i=n-1;i>0;i--)
       {
           int target=int(Random::get(0.0,0.99)*(i+1));
           int temp=p[i];
           p[i]=p[target];
           p[target]=temp;
       }
   }
    int*perlin_generate_perm()
    {
        int*p=new int[256];
        for(int i=0;i<256;i++)
        {
            p[i]=i;
        }
        permute(p,256);
        return p;
    }
    float turb(const QVector3D&p,int depth=7)const
    {
        float accum=0;
        QVector3D temp_p=p;
        float weight=1.0;
        for(int i=0;i<depth;i++)
        {
            accum+=weight*noise(temp_p);
            weight*=0.5;
            temp_p*=2;
        }
        return fabs(accum);
    }

};




#endif // PERLIN_H
